package com.cooper.moviedb.servers;

import com.cooper.java.scraper.*;
import com.cooper.moviedb.bo.MediaEntity;
import com.cooper.moviedb.bo.MediaInfoEntity;
import com.cooper.moviedb.bo.MediaUriEntity;
import com.cooper.moviedb.scraper.FlvcdScraper;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created with IntelliJ IDEA.
 * User: cooperlee
 * Date: 11/15/12
 * Time: 9:04 AM
 * To change this template use File | Settings | File Templates.
 */
public class FlvcdMovieCollection {

    private static final int PAGE_COLLECTION_COUNT = 4;
    private static final int CHECK_INTERVAL = 4;
    private static final int BATCH_UPDATE_COUNT = 10;


    class DetailsCollectionThread implements Runnable {


        private final String YOUKU_REG = "http:\\/\\/v\\.youku\\.com.*";
        private final String LETV_REG = "http:\\/\\/www\\.letv\\.com.*";
        private final String M1905_REG = "http:\\/\\/www\\.m1905\\.com.*";
        private final String PPTV_REG = "http:\\/\\/v\\.pptv\\.com.*";
        private final String QIYI_REG = "http:\\/\\/www\\.iqiyi\\.com.*";
        private final String QQ_REG = "http:\\/\\/v\\.qq\\.com.*";
        private final String SINA_REG = "http:\\/\\/video\\.sina\\.com\\.cn.*";
        private final String SOHU_REG = "http:\\/\\/tv\\.sohu\\.com.*";
        private final String TUDOU_REG = "http:\\/\\/www\\.tudou\\.com.*";
        private final String YUGAOPIAN_REG = "http:\\/\\/www\\.yugaopian\\.com\\/show.*";
        private final String MTIME_REG = "http:\\/\\/.*?\\.mtime\\..*";

        private boolean compareMedia(MediaEntity exisMedia, MediaEntity newMedia) {
            boolean result = false;
            for (MediaUriEntity mue : newMedia.getMediaUriEntitySet()) {
                boolean urlExists = false;
                for (MediaUriEntity emue : exisMedia.getMediaUriEntitySet()) {
                    if (emue.getUrl().equals(mue.getUrl())) {
                        urlExists = true;
                        break;
                    }
                }
                if (!urlExists) {
                    exisMedia.getMediaUriEntitySet().add(new MediaUriEntity(exisMedia, mue.getUrl(), new Timestamp(new Date().getTime()), false));
                    result = true;
                }
            }
            //System.out.println("compare--" + exisMedia.getDetailsTitle());
            return validPlayUrl(exisMedia) || result;
        }

        private boolean validPlayUrl(MediaEntity mediaEntity) {

            boolean result = false;
            boolean havePlay = false;
            for (MediaUriEntity mue : mediaEntity.getMediaUriEntitySet()) {
                boolean urlExists = false;
                try {
                    urlExists = httpDownload.urlExists(mue.getUrl());
                    //System.out.println("validurl--" + mue.getUrl() + "|" + mediaEntity.getDetailsTitle() + "|" + urlExists);
                    if (urlExists) {
                        //TODO valid mediaEntry  url is exists
                        if (mue.getUrl().matches(PPTV_REG)) {
                            String page = httpDownload.downloadPage(mue.getUrl());
                            if (page != null) {
                                if (Pattern.compile("<a[^>]*>VIP尊享<\\/a>\\s*>\\s*<a[^>]*>付费影片<\\/a>").matcher(page).find()) {
                                    urlExists = false;
                                } else {
                                    Matcher m = Pattern.compile("var webcfg =\\s*\\{\"id\":([^,]+),\"src_id\":").matcher(page);
                                    if (m.find()) {
                                        page = httpDownload.downloadPage("http://web-play.pptv.com/webplay4-0-" + m.group(1).trim() + ".xml");
                                        if (Pattern.compile("<error code=\".+\"[^>]*>").matcher(page).find()) {
                                            urlExists = false;
                                        }

                                    } else {
                                        urlExists = false;
                                    }
                                }

                            } else if (page == null || "".equals(page.trim())) {
                                urlExists = false;
                            }

                        }
                        if (mue.getUrl().matches(TUDOU_REG)) {
                            String page = httpDownload.downloadPage(mue.getUrl(), "gbk");
                            if (page != null) {
                                if (Pattern.compile("<i\\s+class=\"face\">:\\(</i><h6[^>]*>对不起，该视频无法播放</h6>").matcher(page).find()) {
                                    urlExists = false;
                                }
                            } else if (page == null || "".equals(page.trim())) {
                                urlExists = false;
                            }
                        }
                        if (mue.getUrl().matches(QQ_REG)) {
                            String page = httpDownload.downloadPage(mue.getUrl());
                            if (page != null) {
                                if (Pattern.compile("<div class=\"mod_error_message\">\\s*<h2>很抱歉，您访问的视频不存在。</h2>").matcher(page).find()) {
                                    urlExists = false;
                                }
                            } else if (page == null || "".equals(page.trim())) {
                                urlExists = false;
                            }
                        }
                        if (mue.getUrl().matches(SINA_REG)) {
                            String page = httpDownload.downloadPage(mue.getUrl());
                            if (page != null) {
                                if (Pattern.compile("<b>呃~对不起，这个页面已经木有啦！</b>\\s*<br><b>5秒钟以后跳转到新浪大片首页！</b>").matcher(page).find()) {
                                    urlExists = false;
                                }
                            } else if (page == null || "".equals(page.trim())) {
                                urlExists = false;
                            }
                        }

                        if (mue.getUrl().matches(M1905_REG)) {

                            String page = httpDownload.downloadPage(mue.getUrl());
                            if (page != null) {
                                if (Pattern.compile("<\\!--FLV播放器部分开始-->\\s*<script>alert\\(\"很抱歉，该影片尚未上线,敬请期待！\"\\);</script>\\s*<\\!--FLV播放器部分结束-->").matcher(page).find()) {
                                    urlExists = false;
                                }
                            } else if (page == null || "".equals(page.trim())) {
                                urlExists = false;
                            }
                        }

                        if (mue.getUrl().matches(QIYI_REG)) {
                            String page = httpDownload.downloadPage(mue.getUrl());
                            if (page != null) {
                                if (Pattern.compile("freeTry\\s*\":\\{\\s*\"arr\"\\s*:\\s*\\[\\s*\\{'content':'您可以免费观看[^']*'.*\\s*\\{'content':'立即购买'").matcher(page).find()) {
                                    urlExists = false;
                                }
                            } else if (page == null || "".equals(page.trim())) {
                                urlExists = false;
                            }
                        }

                    }
                } catch (IOException e) {
                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                } catch (DownloadException e) {
                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                }

                if (!urlExists) {
                    System.out.println("url not Exists:" + mue.getUrl());
                }
                if ((!mue.isDeleted()) != urlExists) {
                    mue.setDeleted(!urlExists);
                    result = true;
                }
                if (urlExists) {
                    havePlay = true;
                }
            }
            if ((!mediaEntity.isDeleted()) != havePlay) {
                mediaEntity.setDeleted(!havePlay);
                result = true;
            }

            return result;
        }


        private boolean vaildMedia(MediaEntity mediaEntity) {
            boolean result = true;
            if (mediaEntity.getMediaUriEntitySet().isEmpty()) {
                return false;
            }
            String title = mediaEntity.getDetailsTitle();
            if (title != null) {
                if (title.contains("预告片") || title.contains("主题曲mv") ||
                        title.contains("宣传片") || title.contains("剧场预告")) {
                    return false;
                }
            }
            return result;
        }

        private List<String> existsCheckIds = new ArrayList<String>();

        private void updateCheckDates() {
            Session session = sessionFactory.openSession();
            try {
                updateCheckDates(session);
            } finally {
                session.close();
            }
        }

        private void updateCheckDates(Session session) {
            if (!existsCheckIds.isEmpty()) {
                String updateSql = "update from MediaEntity set checkDate =? where 1=2 ";
                for (String updateId : existsCheckIds) {
                    updateSql += " or id = '" + updateId + "'";
                }
                System.out.println("begin update checkdata");
                Transaction tx = session.beginTransaction();
                try {
                    session.createQuery(updateSql).setTimestamp(0, new Date()).executeUpdate();
                    tx.commit();
                    System.out.println("update checkdata complete");
                } catch (Exception e) {
                    tx.rollback();
                }
                existsCheckIds.clear();
            }
        }

        @Override
        public void run() {


            FlvcdScraper scraper = createScraper();

            while (!Thread.currentThread().isInterrupted()) {

                MediaEntity mediaEntity = getMedia();

                while (mediaEntity == null) {
                    if (pageComplete()) {
                        updateCheckDates();
                        return;
                    }

                    try {
                        Thread.sleep(1000);
                        mediaEntity = getMedia();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        updateCheckDates();
                        return;
                    }
                }

                try {
                    String detailsPage = httpDownload.downloadPage(mediaEntity.getDetailsUrl(), mediaEntity.getDetailsPageEncode());

                    String detailsXml = scraper.extractDetails(detailsPage);

                    SAXReader reader = new SAXReader();

                    Document document = reader.read(new ByteArrayInputStream(detailsXml.getBytes()));
                    for (java.util.Iterator i = document.getRootElement().elementIterator(); i.hasNext(); ) {
                        Element foo = (Element) i.next();
                        mediaEntity.getMediaInfoEntitySet().add(new MediaInfoEntity(mediaEntity, foo.getName(), foo.getText()));
                    }

                    mediaEntity.setJoinTime(new Timestamp(new Date().getTime()));
                    mediaEntity.putSrcUrls();


                    if (vaildMedia(mediaEntity)) {

                        Session session = sessionFactory.openSession();
                        try {


                            MediaEntity exisMedia = (MediaEntity) session.get(mediaEntity.getClass(), mediaEntity.getId());

                            if (exisMedia == null) {

                                String queryStr = "from MediaUriEntity where 1=2 ";
                                for (MediaUriEntity mediaUriEntity : mediaEntity.getMediaUriEntitySet()) {
                                    queryStr += " or url = '" + mediaUriEntity.getUrl() + "'";
                                }

                                List<MediaUriEntity> uriEntityList = session.createQuery(queryStr).list();
                                if (!uriEntityList.isEmpty()) {
                                    exisMedia = uriEntityList.get(0).getMediaEntity();
                                    for (MediaUriEntity newUrlEntity : mediaEntity.getMediaUriEntitySet()) {
                                        if (!exisMedia.existsUrl(newUrlEntity.getUrl())) {
                                            exisMedia.getMediaUriEntitySet().add(new MediaUriEntity(exisMedia, newUrlEntity.getUrl(), new Timestamp(new Date().getTime()), false));
                                        }

                                    }
                                    validPlayUrl(exisMedia);
                                    exisMedia.setCheckDate(new Timestamp(new Date().getTime()));
                                    exisMedia.setLastChangeDate(new Timestamp(new Date().getTime()));
                                    Transaction tx = session.beginTransaction();
                                    session.update(exisMedia);
                                    tx.commit();
                                } else {


                                    validPlayUrl(mediaEntity);
                                    mediaEntity.setPage(-1);
                                    mediaEntity.setCheckDate(new Timestamp(new Date().getTime()));
                                    mediaEntity.setLastChangeDate(new Timestamp(new Date().getTime()));
                                    Transaction tx = session.beginTransaction();
                                    session.save(mediaEntity);
                                    tx.commit();
                                }
                            } else {
                                System.out.println(exisMedia.getDetailsTitle());
                                boolean updateded = false;
                                if (compareMedia(exisMedia, mediaEntity)) {
                                    if (exisMedia.getPage() <= 0) {
                                        exisMedia.setJoinTime(new Timestamp(new Date().getTime()));
                                    }
                                    exisMedia.setCheckDate(new Timestamp(new Date().getTime()));
                                    exisMedia.setLastChangeDate(new Timestamp(new Date().getTime()));
                                    Transaction tx = session.beginTransaction();
                                    session.update(exisMedia);
                                    tx.commit();
                                    updateded = true;
                                }
                                if (!updateded) {
                                    existsCheckIds.add(exisMedia.getId());
                                    System.out.println("put checkid:" + exisMedia.getId() + "size:" + existsCheckIds.size());
                                    if (existsCheckIds.size() >= BATCH_UPDATE_COUNT) {
                                        updateCheckDates(session);
                                    }
                                }
                            }

                        } finally {
                            session.close();
                        }


                        System.out.println("movie is saved ");
                    }
                } catch (DocumentException e) {
                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.


                } catch (ScraperException e) {
                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.

                } catch (DownloadException e) {
                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                }


            }

            updateCheckDates();

        }
    }

    class PageCollectionThread implements Runnable {

        private static final int MAX_MEDIA_WAIT_COUNT = 700;

        private boolean vaildMedia(String title) {
            boolean result = true;

            if (title != null) {
                if (title.contains("预告片") || title.contains("主题曲mv") ||
                        title.contains("宣传片") || title.contains("剧场预告")) {
                    return false;
                }
            }
            return result;
        }

        @Override
        public void run() {
            FlvcdScraper scraper = createScraper();
            Integer page = getPage();
            while (!Thread.currentThread().isInterrupted() && (page != null)) {
                try {
                    System.out.println("begin down page:" + page);
                    ResultUrl rurl = scraper.createBrowseUrl(page);
                    String sPage = httpDownload.downloadPage(rurl.getUrl(), rurl.getEncodingType());

                    List<MediaEntity> collectionMedias = scraper.extractBrowseResource(sPage);
                    System.out.println("begin down 1");

                    List<MediaEntity> medias = new ArrayList<MediaEntity>();

                    if (!collectionMedias.isEmpty()) {
                        for (MediaEntity media : collectionMedias) {
                            if (vaildMedia(media.getDetailsTitle())) {
                                medias.add(media);
                            }
                        }
                    }

                    if (!medias.isEmpty()) {


                        Session session = sessionFactory.openSession();
                        try {
                            System.out.println("begin down 2");
                            String sqlWhere = " where 1=2 ";
                            for (MediaEntity media : medias) {
                                sqlWhere += " or id = '" + media.getId() + "'";
                            }

                            System.out.println("valid sql where:" + sqlWhere);

                            List<MediaEntity> databaseMedias = session.createQuery("from MediaEntity " + sqlWhere).list();
                            System.out.println("begin down 3");
                            for (MediaEntity databaseMedia : databaseMedias) {
                                if ((databaseMedia.getCheckDate() != null) &&
                                        ((new Date().getTime() - databaseMedia.getCheckDate().getTime()) / 1000 / 60 / 60 / 24) < CHECK_INTERVAL) {
                                    if ((databaseMedia.getLastChangeDate() == null) ||
                                            ((new Date().getTime() - databaseMedia.getLastChangeDate().getTime() / 1000 / 60 / 60 / 24) > CHECK_INTERVAL)) {
                                        medias.remove(databaseMedia);
                                        System.out.println("remove check for time:" + databaseMedia.getId() + "|" + databaseMedia.getCheckDate());
                                    }
                                }
                            }
                        } finally {
                            session.close();
                        }
                        System.out.println("begin down 4");
                        if (!medias.isEmpty())
                            putMediaEntitys(medias);
                    }
                    System.out.println("down complete page:" + page);
                } catch (DownloadException e) {
                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                } catch (ScraperException e) {
                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                }

                while (medias.size() > MAX_MEDIA_WAIT_COUNT) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {

                        return;
                    }
                }

                page = getPage();
            }
            pcc++;
            System.out.println("Thread is complete");
        }
    }

    // private Timestamp joinTime;

    private final Lock lock = new ReentrantLock();

    private List<MediaEntity> medias = new ArrayList<MediaEntity>();

    private List<Integer> pages = new ArrayList<Integer>();

    private List<Integer> completePages = new ArrayList<Integer>();

    private void putCompletePage(Integer page) {
        lock.lock();
        try {
            completePages.add(page);
        } finally {
            lock.unlock();
        }
    }

    private Integer getPage() {
        lock.lock();
        try {
            if (pages.isEmpty()) {
                return null;
            } else {
                Integer result = pages.get(0);
                pages.remove(0);
                return result;
            }
        } finally {
            lock.unlock();
        }
    }


    private MediaEntity getMedia() {
        lock.lock();
        try {
            if (medias.isEmpty()) {
                return null;
            } else {
                MediaEntity result = medias.get(0);
                medias.remove(0);
                return result;
            }

        } finally {
            lock.unlock();
        }
    }

    private void putMediaEntitys(List<MediaEntity> mediaEntitys) {
        lock.lock();
        try {
            medias.addAll(mediaEntitys);
            System.out.println("add Entry" + medias.size());
        } finally {
            lock.unlock();
        }
    }


    private HttpDownload httpDownload;
    private DownloadParams downloadParams;


    public void setDownloadParams(DownloadParams downloadParams) {
        this.downloadParams = downloadParams;
    }

    public void setHttpDownload(HttpDownload httpDownload) {
        this.httpDownload = httpDownload;
    }

    private FlvcdScraper createScraper() {
        return new FlvcdScraper(scraperFile, downloadParams);
    }

    public void generatePages() throws DownloadException, ScraperException {
        FlvcdScraper scraper = createScraper();

        ResultUrl rurl = scraper.createBrowseUrl(1);
        String page = httpDownload.downloadPage(rurl.getUrl(), rurl.getEncodingType());
        // System.out.println( scraper.getBrowsePageCount(page));
        pages.clear();
        completePages.clear();
        for (int i = scraper.getBrowsePageCount(page); i >= 1; i--) {
            pages.add(i);
        }
    }

    public void generatePage(int toPage) {
        pages.clear();
        completePages.clear();
        for (int i = toPage; i >= 1; i--) {
            pages.add(i);
        }

    }

    public void generateSinglePage(int page) {
        pages.clear();
        completePages.clear();
        pages.add(page);
    }

    private File scraperFile;

    public void init(String fileName) {
        scraperFile = new File(fileName);
    }

    private boolean pageComplete() {
        return pcc >= PAGE_COLLECTION_COUNT;
    }

    private int pcc;

    public void collection() {
        pcc = 0;
        for (int i = 0; i < PAGE_COLLECTION_COUNT; i++)
            new Thread(new PageCollectionThread()).start();

        for (int i = 0; i < PAGE_COLLECTION_COUNT * 10; i++)
            new Thread(new DetailsCollectionThread()).start();


    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    private SessionFactory sessionFactory;


}
